﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace UtilZ.Dotnet.Ex.Model
{
    /// <summary>
    /// 释放项
    /// </summary>
    unsafe public class DisposablerItem : IDisposablerItem
    {
        /// <summary>
        /// 释放顺序
        /// </summary>
        public int DisposeOrder
        {
            get { return _disposeOrder; }
        }

        private readonly int _disposeOrder;
        private readonly DisposablerType _disposeType;
        private IDisposable _disposable = null;
        private void* _ptr = null;
        private IntPtr _intPtr = IntPtr.Zero;
        private readonly Type _structType = null;
        private readonly GCHandle? _handle = null;



        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="disposeType">释放类型</param>
        /// <param name="disposeOrder">释放顺序</param>
        private DisposablerItem(DisposablerType disposeType, int disposeOrder)
        {
            this._disposeType = disposeType;
            this._disposeOrder = disposeOrder;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="handle">GCHandle</param>
        /// <param name="disposeOrder">释放顺序</param>
        public DisposablerItem(ref GCHandle handle, int disposeOrder = -1)
             : this(DisposablerType.GCHandle, disposeOrder)
        {
            this._handle = handle;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="disposable">IDisposable</param>
        /// <param name="disposeOrder">释放顺序</param>
        public DisposablerItem(IDisposable disposable, int disposeOrder = -1)
            : this(DisposablerType.IDispose, disposeOrder)
        {
            this._disposable = disposable;
        }


        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="ptr">数据指针</param>
        /// <param name="disposeOrder">释放顺序</param>
        public DisposablerItem(void* ptr, int disposeOrder = -1)
            : this(DisposablerType.Ptr, disposeOrder)
        {
            this._ptr = ptr;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="ptr">非结构体数据指针</param>
        /// <param name="disposeOrder">释放顺序</param>
        public DisposablerItem(IntPtr ptr, int disposeOrder = -1)
            : this(DisposablerType.HGlobalPtr, disposeOrder)
        {
            this._intPtr = ptr;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="ptr">结构体数据指针</param>
        /// <param name="structType">结构体类型</param>
        /// <param name="disposeOrder">释放顺序</param>
        public DisposablerItem(IntPtr ptr, Type structType, int disposeOrder = -1)
            : this(DisposablerType.StructurePtr, disposeOrder)
        {
            this._intPtr = ptr;
            this._structType = structType;
        }





        private List<IDisposable> _childDisposableList = null;
        /// <summary>
        /// 添加一个子释放项
        /// </summary>
        /// <param name="disposable">子释放项</param>
        public void AddChild(IDisposable disposable)
        {
            if (disposable == null)
            {
                throw new ArgumentNullException(nameof(disposable));
            }

            if (this._childDisposableList == null)
            {
                this._childDisposableList = new List<IDisposable>();
            }

            this._childDisposableList.Add(disposable);
        }

        /// <summary>
        /// Dispose
        /// </summary>
        /// <exception cref="NotImplementedException"></exception>
        public void Dispose()
        {
            switch (this._disposeType)
            {
                case DisposablerType.IDispose:
                    this._disposable.Dispose();
                    break;
                case DisposablerType.Ptr:
                    NativeMemory.Free(this._ptr);
                    break;
                case DisposablerType.HGlobalPtr:
                    Marshal.FreeHGlobal(this._intPtr);
                    this._intPtr = IntPtr.Zero;
                    break;
                case DisposablerType.StructurePtr:
                    Marshal.DestroyStructure(this._intPtr, this._structType);
                    Marshal.FreeHGlobal(this._intPtr);
                    this._intPtr = IntPtr.Zero;
                    break;
                case DisposablerType.GCHandle:
                    this._handle.Value.Free();
                    break;
                default:
                    throw new NotImplementedException(this._disposeType.ToString());
            }

            if (this._childDisposableList != null)
            {
                foreach (var disposable in this._childDisposableList)
                {
                    disposable.Dispose();
                }
                this._childDisposableList.Clear();
            }

        }


    }


}
